#import cv  
import time
import math
import random
import pygame
import pygame.camera
from pygame.locals import *
import pyganim

from frame_get import get_frame, string2Surface

SCREENSIZE = [1280,720]
#SCREENSIZE = (1040,680)
GRAVITY = 9.81  #m/s^2
BUYOANCY = 6    #m/s^2
WATER_RESIS = 0.1 #L/s

FISH_POSITIONS = []

class Fish(pygame.sprite.Sprite):
    def __init__(self, num, word):    
        pygame.sprite.Sprite.__init__(self)
        self.num = num
        self.img = []
        self.count = 0                  # Used to control duration of frames in sprite animation
        self.point = (0,0)              # Location of mouse click
        self.screensize = SCREENSIZE
        self.direction = 1              # X-Direction(+1:Left-to-Right, -1: Right-to-Left)
        self.clock = pygame.time.Clock()

        self.r_click = False
        self.random_direction = False
        self.interact = False

        for i in range(1, 9):           # Create each fish sprite ('filename of each frame',    duration_of_frame_in_seconds)
            self.img.append(('Images/Fish_GIF/Fish%s_%s.png'%(self.num, i), 0.2))
        self.fsh = pyganim.PygAnimation(self.img)
        
        self.width, self.height = self.fsh.getMaxSize()

        self.x_pos = random.uniform(0,self.screensize[0]-self.width)
        self.y_pos = random.uniform(0,self.screensize[1]-self.height)
                
        self.impulse_vel = random.uniform(60, 100)       # Initial Velocity of fish caused by fin motion
        self.angle = random.uniform(0,60) * math.pi / 180   # Angle fish swims (In Radians)

        self.x_speed = random.choice([-1,1])*(self.impulse_vel * math.cos(self.angle)) # Magnitude of Velocity in x-direction
        self.y_speed = random.choice([-1,1])*(self.impulse_vel * math.sin(self.angle)) # Magnitude of Velocity in y-direction
        self.x_acc = -(WATER_RESIS * self.x_speed)
        ###NOTE: '-' implies fish moving up and '+' implies fish moving down
        if self.y_speed > 0:        #Fish moving Downwards        
            self.y_acc = -(WATER_RESIS * self.y_speed) + (GRAVITY-BUYOANCY)
#            self.fsh.rotate(-self.angle) 
        else:                       #Fish moving upwards      
            self.y_acc = (WATER_RESIS * self.y_speed)-(GRAVITY-BUYOANCY)  
#            self.fsh.rotate(self.angle)
                
        if self.x_speed < 0:
            self.direction *= -1
            self.fsh.flip(True, False)
        
        self.crit_vel = self.impulse_vel / 2.5           # Velocity at which fish beats fin again        
        FISH_POSITIONS.append((self.x_pos, self.y_pos))    #Globally store each fish location
     
        self.fsh.play()                 # Begins playing the fish sprite
 
                    
    # Called in every iteration of the "game" loop, updates each fish.
    def update(self):
        if (self.x_pos + self.width >= self.screensize[0]+150) or (self.x_pos <= -150) or (random.randint(0,1000) == 30): #Fish go off edges before turning. Occasionally the fish turns randomly
            self.x_speed *= -1
            self.direction *= -1
            self.fsh.flip(True, False)   
        if self.y_pos + self.height >= self.screensize[1] or self.y_pos <= 0:
#            self.fsh.rotate(math.copysign(math.fabs(self.angle * 2), self.y_speed * self.x_speed))
            self.y_speed *= -1
        
        self.time_passed = self.clock.tick()    #unit is milliseconds
        self.time = self.time_passed / 1000.0   #unit is seconds    
        
        self.calculate_movement()               #Calculates the change in position of the fish over the recorded time interval
        
#        self.oldCenter = self.fsh.getRect().center
#        print self.oldCenter
#        self.prev_ang = self.angle
#        self.angle = math.atan2(self.y_speed, self.x_speed)
#        self.fsh.rotate((self.angle - self.prev_ang) * math.pi / 180)
#        self.fsh.getRect().center = self.oldCenter
      
        
        self.x_pos += self.d_x
        self.y_pos += self.d_y
        
        FISH_POSITIONS[self.num-1] = (self.x_pos,self.y_pos)   
    
    def calculate_movement(self):
        
        if self.r_click:        #When a right click on screen
            if (math.fabs(self.x_pos +self.width/2 - self.point[0]) > 100 or (math.fabs(self.y_pos + self.height/2 - self.point[1]) > 100)):
                self.dir = math.atan2((self.point[1] - self.y_pos),(self.point[0] - self.x_pos))    #Angle between two points
                self.x_speed = self.impulse_vel * math.cos(self.dir) * 2    #Doubles motion towards mouse click
                self.y_speed = self.impulse_vel * math.sin(self.dir) * 2    
                if (self.direction > 0 and self.x_speed < 0) or (self.direction < 0 and self.x_speed > 0):  #If the click causes the fish to change direction
                    self.fsh.flip(True, False)
                    self.direction *= -1         
            else:           # Now close to mouse click, fish wait until the click is released
                self.x_speed = 0
                self.y_speed = 0
        
            self.d_x = (self.x_speed * self.time)   # Change in fish x_position
            self.d_y = (self.y_speed * self.time)   # Change in fish y_position         
               
        elif self.random_direction:     # Called once right mouse button is released (fish to begin swimming again)
            self.angle = random.uniform(0,60) * math.pi / 180   # Angle fish swims (In Radians)
#            self.fsh.rotate(self.angle)
            self.x_speed = random.choice([-1,1])*(self.impulse_vel * math.cos(self.angle)) 
            self.y_speed = random.choice([-1,1])*(self.impulse_vel * math.sin(self.angle))       
            if (self.direction > 0 and self.x_speed < 0) or (self.direction < 0 and self.x_speed > 0): # If fish is now swimming in the opposite direction
                self.fsh.flip(True, False)
                self.direction *= -1
            self.random_direction = False       # Only occurs once after right mouse button is released 

        elif self.interact:     # If a fish is clicked (Currently only flips fish, but could be developed)
            self.x_speed *= -1
            self.direction *= -1
            self.fsh.flip(True, False)
            self.interact = False
        
        else:       # Normal swimming, Without any interaction from user  
            self.d_x = (self.x_speed * self.time) + (0.5 * self.x_acc * math.pow(self.time,2))  #Current displacement is calculated using previously recorded values
            self.d_y = (self.y_speed * self.time) + (0.5 * self.y_acc * math.pow(self.time,2)) 
    
            if math.fabs(self.x_speed) < self.crit_vel: #Fish is now swimming too slow
                    self.angle = random.uniform(0,60) * math.pi / 180   # Angle fish swims (In Radians)
                    #self.fsh.rotate(self.angle)
                    self.x_speed = math.copysign(self.impulse_vel * math.cos(self.angle), self.x_speed) # Keep swimming in same direction
                    self.y_speed = math.copysign(self.impulse_vel * math.sin(self.angle), self.y_speed)
                    self.count = 0      # Reset the counter
                    self.fsh.rate = 1   # Speed up animation frames to coincide with increased speed                         
            else:
                if self.count > 10:     # Once count has exceeds 10, animation returns to normal speed 
                    self.fsh.rate = 0.2      
                self.x_speed += self.time*self.x_acc    # v=u+at
                self.y_speed -= self.time*self.y_acc    # Since up is negative and down is positive 
                self.count += 1
            
            self.x_acc = -(WATER_RESIS * self.x_speed)
            if self.y_speed > 0:        #Fish moving Downwards        
                self.y_acc = -(WATER_RESIS * self.y_speed) + (GRAVITY-BUYOANCY) 
            else:                       #Fish moving upwards      
                self.y_acc = (WATER_RESIS * self.y_speed)-(GRAVITY-BUYOANCY)
    
    def swim_to_point(self, mouse_point):
        self.point = mouse_point
        self.r_click = True 

    def check_for_interaction(self, point):
        if (math.fabs(self.x_pos + self.width/2 - point[0]) < 50) and (math.fabs(self.y_pos + self.height/2 - point[1]) < 50):
            self.interact = True
        else:
            pass
        
    def left_release(self): 
        self.interact = False
        
    def right_release(self):
        self.r_click = False    
        self.random_direction = True

    def blit(self, screen):             # Pastes fish onto screen
        self.fsh.blit(screen, self.get_pos())
    
    def get_pos(self):
        return (self.x_pos, self.y_pos)

    def make_transparent(self):
        self.fsh.make_transparent(100) # Calls the pyganim.py method

    def reset_colour(self):
        self.fsh.clearTransforms()  # Returns original images (which may need to be flipped)
        if self.x_speed < 0:
            self.fsh.flip(True, False)


class Bubble(pygame.sprite.Sprite):
    def __init__(self, word):    
        pygame.sprite.Sprite.__init__(self)
        self.clock = pygame.time.Clock()
        self.screensize = SCREENSIZE
        self.img = []
        
        for i in range(1, 26):
            self.img.append(('Images/Bubble_GIF/Bubble2_%s.png'%(i), 0.2))   # Load Each Frame
        self.bub = pyganim.PygAnimation(self.img)

        self.width, self.height = self.bub.getMaxSize()

        self.bub.play()     # Play the Bubble Animation
        self.multiplier = SCREENSIZE[1]/self.height
        self.bub.scale([self.width*self.multiplier,self.height*self.multiplier])  # Make Bubbles go to top of screen
        
    def blit(self, screen, pos):     # Paste Bubble animation onto screen
        self.bub.blit(screen, pos)
    
    def make_transparent(self):
        self.bub.make_transparent(100) 
        
    def reset_bubble(self):
        self.bub.clearTransforms()
        self.bub.scale([self.width,SCREENSIZE[0]-200])  # Make Bubbles go to top of screen


class VideoFishTank(object):
    def __init__(self, **argd):
        self.__dict__.update(**argd)
        super(VideoFishTank, self).__init__(**argd)
        self.screen = pygame.display.set_mode(SCREENSIZE)
        pygame.display.set_caption('The SAP Fish Tank') 
        #self.init_camera()        
        self.clk = pygame.time.Clock()
              
    def init_camera(self):
        pygame.camera.init()
        self.cam_list = pygame.camera.list_cameras()         # Gets a list of available cameras       
        if not self.cam_list:
            raise ValueError("Sorry, no cameras detected.") 
        self.cam_id = self.cam_list[0]
        self.cam = pygame.camera.Camera(self.cam_id, SCREENSIZE)
        self.cam.start()
    
    def setup_fishtank(self):   # These remain stationary, therefore can be recalled when resetting fish tank transparency
        ###CODE FOR ANIMATED OCEAN IF USED
        self.img = []
        for i in range(1, 10):
            self.img.append(('Images/Ocean_GIF/ocean%s.png'%(i), 0.2))
        self.ocean = pyganim.PygAnimation(self.img)
        self.ocean.scale(SCREENSIZE)
        self.ocean.make_transparent(100)
        self.ocean.play()
#        self.ocean = pygame.image.load('Images/Ocean_1.png').convert_alpha()    # Load image
#        self.ocean = pygame.transform.scale(self.ocean,SCREENSIZE)      # Ensure image is same size as screen
#        self.ocean.fill((255, 255, 255, 170), None, pygame.BLEND_RGBA_MULT)  # Make slightly transparent (170) to allow for the live video stream to be seen 
                                
        self.foreground = pygame.image.load('Images/Fish_Tank_Bed.png').convert_alpha() 
        self.foreground = pygame.transform.scale(self.foreground,SCREENSIZE)
        self.foreground.fill((255, 255, 255, 255), None, pygame.BLEND_RGBA_MULT)
        
    def setup_fish(self):   #Can only be used for initial setup, as fish are moving 
        self.fish = [Fish(n, None) for n in xrange(1,6)]    #Load the chosen number of fish

    def setup_bubbles(self):
        self.bubble = Bubble(None)  

    def make_transparent(self): #Makes full fish tank overlay transparent
        self.foreground.fill((255, 255, 255, 100), None, pygame.BLEND_RGBA_MULT)
        #self.ocean.fill((255, 255, 255, 150), None, pygame.BLEND_RGBA_MULT) 
        self.ocean.make_transparent(100)
        self.bubble.make_transparent()
        for fsh in self.fish:
            fsh.make_transparent()
            
    def reset_tank(self):   #Remove transparency and return fish tank overlay
        self.setup_fishtank()
        self.bubble.reset_bubble()
        for fsh in self.fish:
            fsh.reset_colour()  
    
    def update(self, queue):   #Iterative Loop
        smallres = (SCREENSIZE[0]/5, SCREENSIZE[1]/5)
        #self.background = self.cam.get_image()  #Captures current video frame
        #self.background = pygame.transform.scale(self.background,SCREENSIZE)    
        background = get_frame(self.address)
				# Pull local frame off queue
        if queue.full():
            smallscreen = queue.get()
            smallscreen = string2Surface(smallscreen)
						# Make it the smaller size
            smallscreen = pygame.transform.scale(smallscreen, smallres)

        #Paste all layers on screen
        self.screen.blit(background,(0,0))
        #self.screen.blit(self.ocean,(0,0))
        self.ocean.blit(self.screen,(0,0))    # Used if Ocean is animated
        self.screen.blit(self.foreground,(0,0))
        self.bubble.blit(self.screen, (100,0))
        for fish in self.fish:
            fish.blit(self.screen)
            fish.update()
            time.sleep(0.01)
        
        self.screen.blit(smallscreen,(SCREENSIZE[0]-smallres[0]-10,SCREENSIZE[1]-smallres[1]-10))
        
        self.clk.tick()
        print "FPS:", self.clk.get_fps()

        pygame.display.update()   
                
    def main(self, addr, que):
        self.setup_fishtank()
        self.setup_fish()
        self.setup_bubbles()
        
        self.address = addr

        while True: #Game Loop - Detects Key Presses and Mouse Clicks 
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.display.quit()
                    exit()
                elif pygame.key.get_pressed()[K_UP]:
                    self.make_transparent()
                elif pygame.key.get_pressed()[K_DOWN]:
                    self.reset_tank()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:   #Left Click
                        for fish in self.fish:
                            fish.check_for_interaction(event.pos)   #Check if click is over a fish
                    elif event.button == 3: #Right Click
                        for fish in self.fish:
                            fish.swim_to_point(event.pos)   #Get all fish to swim towards point
                elif event.type == pygame.MOUSEBUTTONUP:    #After mouse button has been released
                    if event.button == 1:
                        for fish in self.fish:
                            fish.left_release()                    
                    if event.button == 3:
                        for fish in self.fish:
                            fish.right_release()                    
            self.update(que)          
            
def main():  
    pygame.init()
    VideoFishTank().main()
    pygame.quit()

if __name__ == "__main__":
    main()

